package helper

import (
	"context"
	"encoding/base64"
	"fmt"
	"os"
	"strings"
	"time"
	"unicode/utf8"

	byoneconfig "code.bydev.io/cht/customer/kyc_admin_service/internal/config"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/config"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/constdata"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/utils/aes256"
	"code.bydev.io/frameworks/sechub-go/api"
	"code.bydev.io/frameworks/sechub-go/api/secrypto"
	"git.bybit.com/svc/mod/pkg/bsecret"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/google/uuid"
	jsoniter "github.com/json-iterator/go"
	"go.uber.org/zap"
)

func IsJPNOrCHN(name string) bool {
	ret := true
	for len(name) > 0 {
		r, size := utf8.DecodeLastRuneInString(name)
		if size == 1 && r == 0x20 {
			name = name[:len(name)-size]
			continue
		}
		if !((r >= 0x2E80 && r <= 0x2FDF) ||
			(r >= 0x2FF0 && r <= 0x30FF) ||
			(r >= 0x31C0 && r <= 0x4DFF) ||
			(r >= 0x4E00 && r <= 0x9FBF) ||
			(r >= 0xF900 && r <= 0xFAFF) ||
			(r >= 0xFE30 && r <= 0xFE4F)) {
			ret = false
		}
		name = name[:len(name)-size]
	}
	return ret
}

func TimeCost(log *zap.Logger, tag string) func() {
	start := time.Now()
	return func() {
		tc := time.Since(start)
		if log != nil {
			log.Info(fmt.Sprintf("%s timecost= %v\n", tag, tc))
		}
	}
}

func DecryptStringWithBase64(cipherText string, dataKey bsecret.DataKey) (string, error) {
	blob, err := base64.StdEncoding.DecodeString(cipherText)
	if err != nil {
		return "", err
	}
	target, err := dataKey.Decrypt(nil, blob, 0)
	if err != nil {
		return "", err
	}
	return string(target), nil
}

func EncryptStringWithBase64(raw string, dataKey bsecret.DataKey) (string, error) {
	target, err := dataKey.Encrypt(nil, []byte(raw), 0)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(target), nil
}

func EncryptStringWithBase64Test(raw string, dataKey bsecret.DataKey) (string, error) {
	return raw, nil
}

// DeSecHubCodeString Decrypt deStr by SecHub
func DeSecHubCodeString(secHub *api.Sechub, enStr string) (string, error) {
	if enStr == "" {
		return "", nil
	}
	secKey, err := secHub.GetEncryptKey("aes")
	if err != nil {
		return enStr, err
	}
	res, err := secrypto.Decrypt("ase256gcm", secKey, enStr)
	if err != nil {
		return enStr, err
	}
	return res, nil
}

// EnSecHubCodeString Encrypt deStr by SecHub
func EnSecHubCodeString(secHub *api.Sechub, deStr string) (string, error) {
	secKey, err := secHub.GetEncryptKey("aes")
	if err != nil {
		return deStr, err
	}
	res, err := secrypto.Encrypt("ase256gcm", secKey, deStr)
	if err != nil {
		return deStr, err
	}
	return res, nil
}

// DeAES256CodeString Decrypt enStr by AES256GCM
func DeAES256CodeString(secHub *api.Sechub, enStr string) (string, error) {
	if enStr == "" {
		return "", nil
	}
	secKey, err := secHub.GetEncryptKey("aes")
	if err != nil {
		return enStr, err
	}
	res, err := aes256.Decrypt(secKey, enStr)
	if err != nil {
		return enStr, err
	}
	return res, nil
}

// EnAES256CodeString Encrypt deStr by AES256GCM
func EnAES256CodeString(secHub *api.Sechub, deStr string) (string, error) {
	secKey, err := secHub.GetEncryptKey("aes")
	if err != nil {
		return deStr, err
	}
	res, err := aes256.Encrypt(secKey, deStr)
	if err != nil {
		return deStr, err
	}
	return res, nil
}

func CountryCode2Alpha3(alpha2 string) string {
	codeMAp := constdata.GetCountryCodeMap()
	alpha3, ok := codeMAp[alpha2]
	if ok {
		return alpha3.(string)
	}
	return alpha2
}

type CountryName struct {
	EnName string `json:"enName"`
	CnName string `json:"cnName"`
}

func CountryNameAlpha3(alpha3 string) CountryName {
	cn := CountryName{EnName: alpha3, CnName: alpha3}
	if alpha3 != "" {
		codeMAp := constdata.GetCountryNameMap()
		name, ok := codeMAp[alpha3]
		if ok {
			mn, err := jsoniter.MarshalToString(name)
			if err != nil {
				return cn
			}
			err = jsoniter.UnmarshalFromString(mn, &cn)
			if err != nil {
				return cn
			}
		}
	}

	return cn
}

func SetReqID(ctx context.Context) context.Context {
	return context.WithValue(ctx, KycAdminReqIDKey, strings.ReplaceAll(uuid.New().String(), "-", ""))
}

func ReqID(ctx context.Context) string {
	if reqID, ok := ctx.Value(KycAdminReqIDKey).(string); ok {
		return reqID
	}
	return ""
}

func GetReqID(ctx context.Context) interface{} {
	return ctx.Value(KycAdminReqIDKey)
}

func IsEnvTest() bool {
	switch os.Getenv("MY_ENV_NAME") {
	case "testnet", "mainnet", "prod":
		return false
	case "dev", "devnet", "qa":
		return true
	}
	return true
}

func GetAWSS3Session() (*session.Session, error) {
	var (
		err  error
		sess *session.Session
	)
	s3Conf := byoneconfig.GetAWSS3Config()
	region := config.GetString(config.KeyAWSRegion)

	// Initialize a session in us-west-2 that the SDK will use to load
	// credentials from the shared credentials file ~/.aws/credentials.
	if IsEnvTest() {
		sess, err = session.NewSession(&aws.Config{
			Region:      aws.String(region),
			Credentials: credentials.NewStaticCredentials(s3Conf.AccessKeyID, s3Conf.SecretAccessKey, s3Conf.SessionToken),
		})
	} else {
		sess, err = session.NewSession(&aws.Config{
			Region: aws.String(region)},
		)
	}

	return sess, err
}
